package controllers

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"proxy_resource_test/bootstrap/models"
	"proxy_resource_test/bootstrap/server"
	"proxy_resource_test/config"
	"proxy_resource_test/dao"
	"proxy_resource_test/services/pool_ip"
	"proxy_resource_test/services/pool_ip/rsp"
	"proxy_resource_test/utils"
	"proxy_resource_test/utils/resp"
	"strings"
	"time"
)

// AddTask 添加任务
func AddTask(c *gin.Context) {
	cfg, err := getParam(c)
	if err != nil {
		resp.Json(c, 400, "false", map[string]interface{}{"err": err.Error()})
		return
	}
	//传空即为随机地区
	if cfg.Popular == "" {
		cfg.Popular = "random"
	}
	// 防止输错
	switch cfg.OperateName {
	case "brightdata":
		cfg.TargetURL = "https://lumtest.com/myip.json"
	default:
	}
	cfg.Popular = strings.ToLower(cfg.Popular)
	field := map[string]interface{}{
		"operate":        cfg.Operate,
		"total":          cfg.Total,
		"concurrency":    cfg.Concurrency,
		"target_url":     cfg.TargetURL,
		"user_tpl":       cfg.UserTpl,
		"user":           cfg.User,
		"password":       cfg.Password,
		"proxy_addr":     cfg.ProxyAddr,
		"duration":       cfg.Duration,
		"popular":        cfg.Popular,
		"machine_no_min": 0,
		"machine_no_max": 0,
		"operate_name":   cfg.OperateName,
	}
	if cfg.MachineSnMin > 0 && cfg.MachineSnMax > 0 {
		if cfg.MachineSnMax < cfg.MachineSnMin {
			cfg.MachineSnMax = cfg.MachineSnMin
		}
		field["machine_no_min"] = cfg.MachineSnMin
		field["machine_no_max"] = cfg.MachineSnMax
	}
	if dao.GetRedis().HGet(fmt.Sprintf("%s:%s", pool_ip.StatusHashKey, cfg.Operate), "status").Val() != "" {
		resp.Json(c, 200, "false", map[string]interface{}{"err": "task already exists"})
		return
	}
	// 写入任务表, 留痕！！！
	err = cfg.SaveSchedule()
	if err != nil {
		resp.Json(c, 200, "false", map[string]interface{}{"err": err.Error()})
		return
	}
	server.AutoMigrateInfo(cfg.Operate) // 自动迁移建场景表
	//server.AutoMigrateStInfo()

	pipe := dao.GetRedis().Pipeline()
	pipe.HMSet(fmt.Sprintf("%s:%s", pool_ip.ConfigKey, cfg.Operate), field)
	pipe.Expire(fmt.Sprintf("%s:%s", pool_ip.ConfigKey, cfg.Operate), pool_ip.Ttl) // 设置过期时间
	pipe.Del(fmt.Sprintf("%s:%s", pool_ip.TaskCounter, cfg.Operate))               // 初始化执行量计数器
	_, _ = pipe.Exec()
	// 通过 Manager 启动
	pool_ip.Manager.Start(cfg.Operate, cfg.Popular, &cfg)
	resp.Json(c, 200, "created_success", map[string]interface{}{"task": field["operate"], "id": cfg.Id})
}

// 公共获取方法
func getParam(c *gin.Context) (cfg models.TaskConfig, err error) {
	if config.AppConfig.Development != "master" {
		err = fmt.Errorf("auth fail")
		return
	}
	if err = c.Bind(&cfg); err != nil {
		err = fmt.Errorf("param %s", err.Error())
		return
	}
	// 检查并补充 UserTpl
	if !strings.HasPrefix(cfg.UserTpl, "http") {
		cfg.UserTpl = "http://" + cfg.UserTpl
	}
	//如果存在时长就不判断个数，否则需判断并矫正个数
	if cfg.Duration > 0 {
		cfg.Duration = cfg.Duration*3600 + time.Now().Unix()
		cfg.Total = 0
	} else {
		if cfg.Total < 1 {
			cfg.Total = 1000000
		}
		cfg.Duration = 0
	}
	if cfg.Concurrency < 1 {
		cfg.Concurrency = 10
	}

	if strings.Index(cfg.User, ":") == -1 {
		err = fmt.Errorf("user format error")
		return
	}

	cfg.Operate = strings.TrimSpace(cfg.Operate)
	cfg.User = utils.RemoveSpaces(cfg.User)
	cfg.Password = utils.RemoveSpaces(cfg.Password)
	cfg.ProxyAddr = utils.RemoveSpaces(cfg.ProxyAddr)

	if cfg.Popular == "random" && strings.Contains(cfg.User, "<region>") {
		return cfg, fmt.Errorf("user format error--do not support random and region")
	}
	if len(cfg.OperateName) < 1 {
		return cfg, fmt.Errorf("must be set operate_name")
	}
	return cfg, nil
}

func TestTask(c *gin.Context) {
	cfg, err := getParam(c)
	if err != nil {
		resp.Json(c, 400, "false", map[string]interface{}{"err": err.Error()})
		return
	}
	var country = cfg.Popular
	// 采用轮训
	if len(country) > 0 {
		switch country {
		case "pop":
			iter := pool_ip.NewPopularIter(country)
			country = iter.Next()
		case "eu_pop":
			iter := pool_ip.NewPopularIter(country)
			country = iter.Next()
		}
	}
	task := rsp.TaskRspGather{
		ProxyAddr:   cfg.ProxyAddr,
		UserTpl:     cfg.UserTpl,
		User:        cfg.User,
		Password:    cfg.Password,
		TargetStr:   cfg.TargetURL,
		OperateName: cfg.OperateName,
	}

	err, sec, infoIp := task.PerformRequest(country)
	status := "success"
	if err != nil {
		status = "fail"
	}
	resp.Json(c, 200, status, map[string]interface{}{
		"ip_info": infoIp,
		"err":     err,
		"sec":     sec,
	})
}

// StatusTask 任务状态
func StatusTask(c *gin.Context) {
	var opt models.StatisticsParam
	if err := c.ShouldBind(&opt); err != nil {
		resp.Json(c, 400, "false", map[string]interface{}{"err": err.Error()})
		return
	}
	stats := dao.GetRedis().HGetAll(fmt.Sprintf("%s:%s", pool_ip.StatusHashKey, opt.Operate)).Val()
	resp.Json(c, 200, "success", map[string]interface{}{"status": stats})
}

func ControlHandler(cmd string) gin.HandlerFunc {
	return func(c *gin.Context) {
		if config.AppConfig.Development != "master" {
			resp.Json(c, 403, "false", map[string]interface{}{"err": "auth fail"})
			return
		}
		var opt models.StatisticsParam
		if err := c.ShouldBind(&opt); err != nil {
			resp.Json(c, 400, "false", map[string]interface{}{"err": err.Error()})
			return
		}
		exists, _ := dao.GetRedis().Exists(fmt.Sprintf("%s:%s", pool_ip.ConfigKey, opt.Operate)).Result()
		if exists == 0 {
			resp.Json(c, http.StatusNotFound, "task_not_found", nil)
			return
		}
		// 检测是否在运行中
		stats := dao.GetRedis().HGetAll(fmt.Sprintf("%s:%s", pool_ip.StatusHashKey, opt.Operate)).Val()
		if stats["status"] == "completed" {
			resp.Json(c, 200, "err", map[string]interface{}{"err": "task completed"})
			return
		}
		// 查询国家状态
		pop := dao.GetRedis().HGet(fmt.Sprintf("%s:%s", pool_ip.ConfigKey, opt.Operate), "popular").Val()
		switch cmd {
		case pool_ip.CmdStart:
			cfg, _ := pool_ip.InitOrFetchConfig(opt.Operate)
			pool_ip.Manager.Start(opt.Operate, pop, cfg)
		case pool_ip.CmdStop:
			pool_ip.Manager.Stop(opt.Operate)
		default:
			resp.Json(c, 400, "false", gin.H{"err": "invalid cmd"})
			return
		}
		resp.Json(c, 200, "success", map[string]interface{}{"cmd": cmd})
	}
}

// GetStatic 获取统计分数区间内的个数
func GetStatic(c *gin.Context) {
	var opt models.StatisticsParam
	if err := c.ShouldBind(&opt); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if config.AppConfig.Development != "master" {
		resp.Json(c, 403, "false", map[string]interface{}{"err": "auth fail"})
		return
	}
	list, err := server.GetTaskStatistics(opt)
	if err != nil {
		resp.Json(c, 200, "false", map[string]interface{}{"err": err.Error()})
		return
	}
	resp.Json(c, 200, "success", map[string]interface{}{
		"data": list,
	})
}
